/*
 * Copyright 2013 Philip Luyckx
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package be.pluyckx.batteryprogress.service;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast;

import java.io.IOException;
import java.util.Date;

import be.pluyckx.batteryprogress.R;
import be.pluyckx.batteryprogress.activity.MainActivity;
import be.pluyckx.batteryprogress.application.Constants;
import be.pluyckx.batteryprogress.data.history.BatteryHistory;
import be.pluyckx.batteryprogress.data.history.BatteryLevel;
import be.pluyckx.batteryprogress.data.history.snapshot.ProcessSnapshot;
import be.pluyckx.batteryprogress.receiver.BatteryStateReceiver;
import be.pluyckx.batteryprogress.utils.calculator.AverageTimeProgressCalculator;
import be.pluyckx.batteryprogress.utils.calculator.BatteryHistoryWrapper;
import be.pluyckx.batteryprogress.utils.calculator.ProgressCalculator;

/**
 * Created by pluyckx on 7/30/13.
 */
public class BatteryProgressService extends Service {
  private static final String TAG = "BatteryProgressService";

  public static final String EXTRA_START_TYPE = "start_type";

  public static final int START_TYPE_BOOT = 0;
  public static final int START_TYPE_BIND = 1;
  public static final int START_TYPE_LEVEL = 2;
  public static final int START_TYPE_CHARGING = 3;
  public static final int START_TYPE_DISCHARGING = 4;

  public static final String EXTRA_LEVEL = "level";
  public static final String EXTRA_SCALE = "scale";
  public static final String EXTRA_CHARGING = "charging";
  public static final String EXTRA_DISCHARGING = "discharging";

  private static final Handler mThreadHandler = new Handler();
  private static BatteryProgressService service = null;
  private ServiceBinder mBinder;
  private BatteryStateReceiver mBatteryStateReceiver;
  private ProgressCalculator mCalculator = new AverageTimeProgressCalculator();
  private ProgressCalculator.Parameters mCalculatorParameters = new ProgressCalculator.Parameters(ProgressCalculator.Parameters.ORDERED_OLD_LOWEST_INDEX);

  public static BatteryProgressService getService() {
    return service;
  }

  public static Intent createIntentBatteryLevel(Context context, int batteryLevel, int scale, boolean charging, boolean discharging) {
    Intent i = new Intent(context, BatteryProgressService.class);
    i.putExtra(EXTRA_LEVEL, batteryLevel);
    i.putExtra(EXTRA_SCALE, scale);
    i.putExtra(EXTRA_START_TYPE, START_TYPE_LEVEL);
    i.putExtra(EXTRA_CHARGING, charging);
    i.putExtra(EXTRA_DISCHARGING, discharging);

    return i;
  }

  public static Intent createIntentBoot(Context context) {
    Intent i = new Intent(context, BatteryProgressService.class);
    i.putExtra(EXTRA_START_TYPE, START_TYPE_BOOT);

    return i;
  }

  public static Intent createIntentBind(Context context) {
    Intent i = new Intent(context, BatteryProgressService.class);
    i.putExtra(EXTRA_START_TYPE, START_TYPE_BIND);

    return i;
  }

  @Override
  public void onCreate() {
    super.onCreate();

    service = this;

    IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
    mBatteryStateReceiver = new BatteryStateReceiver();
    registerReceiver(mBatteryStateReceiver, filter);

    new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          Constants.getBatteryHistoryDatabase().read();
          mThreadHandler.post(new Runnable() {
            @Override
            public void run() {
              Toast.makeText(getApplicationContext(), R.string.err_history_loaded, Toast.LENGTH_LONG).show();
            }
          });
        } catch (IOException ex) {
          ex.printStackTrace();
          mThreadHandler.post(new Runnable() {
            @Override
            public void run() {
              Toast.makeText(getApplicationContext(), R.string.err_could_not_load_history, Toast.LENGTH_LONG).show();
            }
          });
        }
      }
    }).start();

    Log.i("BatteryProgressService", "Created");
  }

  private void setNotification() {
    startForeground(1, createNotification());
  }

  private Notification createNotification() {
    Intent intent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this.getBaseContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    BatteryHistory history = BatteryHistory.getHistory();
    ProgressCalculator.Result result = mCalculator.calculate(new BatteryHistoryWrapper(history), mCalculatorParameters);
    Date date = null;
    if (result != null) {
      ProgressCalculator.DataItem newest = result.getNewestItem();
      date = new Date(newest.getDate() + result.getSecondsToGo() * 1000);
    }

    RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.view_notification);
    contentView.setImageViewResource(R.id.iv_image, R.drawable.ic_launcher);
    contentView.setTextViewText(R.id.tv_title, getResources().getString(R.string.app_name));
    contentView.setTextViewText(R.id.tv_battery_level, (history.size() > 0 ? history.getCurrent().getBatteryLevelPercentageAsString() : getResources().getString(R.string.unknown)));
    contentView.setTextViewText(R.id.tv_estimation, (date != null ? Constants.dateTimeLongFormat.format(date) : getResources().getString(R.string.unknown)));
    contentView.setTextViewText(R.id.tv_speed, (result != null ? Constants.percentPerHourFormat.format(result.getPercentPerHour()) : getResources().getString(R.string.unknown)));

    Notification notification = new Notification();
    notification.contentView = contentView;
    notification.contentIntent = pendingIntent;
    notification.icon = R.drawable.ic_launcher;

    return notification;
  }

  private void updateNotification() {
    ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(1, createNotification());
  }

  @Override
  public void onDestroy() {
    Constants.historyDump.autoDumpFile(new BatteryHistory(BatteryHistory.getHistory()),
        Constants.getDefaultHistoryDumpCallback());

    unregisterReceiver(mBatteryStateReceiver);

    service = null;

    Log.i("BatteryProgressService", "Destroyed");

    super.onDestroy();
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    setNotification();

    if (intent != null) {
      switch (intent.getIntExtra(EXTRA_START_TYPE, -1)) {
        case START_TYPE_BOOT:
          try {
            Constants.getBatteryHistoryDatabase().clear();
          } catch (IOException e) {
            e.printStackTrace();
          }
          BatteryHistory.getHistory().clear();
          break;
        case START_TYPE_LEVEL:
          addBatteryCapacity(intent.getIntExtra(EXTRA_LEVEL, -1), intent.getIntExtra(EXTRA_SCALE, -1), intent.getBooleanExtra(EXTRA_CHARGING, false), intent.getBooleanExtra(EXTRA_DISCHARGING, true));
          break;
      }
    }

    return super.onStartCommand(intent, flags, startId);
  }

  @Override
  public IBinder onBind(Intent intent) {
    if (this.mBinder == null) {
      mBinder = new ServiceBinder();
    }

    return mBinder;
  }

  private Object lockAdd = new Object();

  private void addBatteryCapacity(final int batteryLevel, final int scale, final boolean charging, final boolean discharging) {
    // changed to currentTimeMillis, nanoTime doesn't work on multi core systems!

    final BatteryHistory history = BatteryHistory.getHistory();
    if (history.size() > 0) {
      BatteryLevel last = history.getLast();
      if (last.getBatteryLevel() == batteryLevel &&
          ((last.getState() == BatteryLevel.STATE_CHARGING && charging) ||
              last.getState() == BatteryLevel.STATE_DISCHARGING && discharging
          )) {
        return;
      }
    }
    synchronized (lockAdd) {
      if (history.size() > 0) {
        BatteryLevel last = history.getLast();
        if (last.getBatteryLevel() == batteryLevel &&
            ((last.getState() == BatteryLevel.STATE_CHARGING && charging) ||
                last.getState() == BatteryLevel.STATE_DISCHARGING && discharging
            )) {
          return;
        }
      }

      if (charging || discharging) {
        final long date = System.currentTimeMillis();
        final long elapsed = date / 1000;
        final BatteryLevel level = new BatteryLevel((discharging ? BatteryLevel.STATE_DISCHARGING : BatteryLevel.STATE_CHARGING),
            date,
            elapsed,
            batteryLevel,
            scale);
        history.add(level);

        ProcessSnapshot snapshot = new ProcessSnapshot();
        updateNotification();
        snapshot.makeSnapshot(this, new ProcessSnapshot.Callback() {
          @Override
          public void onSnapshotReady(ProcessSnapshot snapshot) {
            level.setSnapshot(snapshot);
          }
        });
      }
    }
  }

  public class ServiceBinder extends Binder {
    private ServiceBinder() {

    }

    public BatteryProgressService getService() {
      return BatteryProgressService.this;
    }
  }
}
